//import java.awt.GraphicsConfiguration;
import java.awt.HeadlessException;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.event.ListSelectionEvent;

import java.text.DecimalFormat;
import java.util.*;

import gnu.io.*;
//import javax.comm.CommPortIdentifier;
import java.util.Enumeration;

/**
 * 
 */

/**
 * @author andipandi
 *
 */
public class GatewayFrame extends JFrame implements ActionListener, ListSelectionListener, GatewayListener{
	

	//Getway Communication Interface
	GatewayClient gateway = new GatewayClient(this);
	//list of all nodes
	HashSet<String> nodes = new HashSet<String>();
	Vector<Integer> node_list = new Vector<Integer>();
	
	/**
	 * GUI objects
	 */
	JPanel contentPane = new JPanel();
	JPanel panNorth= new JPanel();
	JPanel panWest= new JPanel();
	JPanel panCenter= new JPanel();
	JPanel panSouth= new JPanel();

	JComboBox  comboPort = new JComboBox();
	//JComboBox  comboPort = new JComboBox(CommPortIdentifier.getPortIdentifiers());
	JList lstNodes = new JList();
	JScrollPane panLstNodes = new JScrollPane(lstNodes);
	JButton btnOpen = new JButton("Open");
	JButton btnConnect = new JButton("Connect");
	JTextField txtStatus = new JTextField("not connected");
	
    String dataInfo[][] = {	{"Node ID","no node selected"},
    					{"Cost",""},
    					{"Type",""},
    					{"Neighbours",""},
    					{"ERRORS",""},	  
    					{"Last error",""},	  
    					{"Warnings",""},	  
    					{"Last warning",""},
    					{"Child time offset (ACLK ticks)",""},
    					{"ADV slot(ACLK ticks)",""}};
    String colInfo[] = {"Node Information",""};
	JTable tblInfo = new JTable(dataInfo, colInfo);
    String dataRx[][] = {	
			{"ADV packets",""},
			{"TIME packets",""},
			//{"DAT packets",""},
			{"vACK packets",""},
			{"eACK packets", ""}};
    String colRx[] = {"Node Reception Statistic",""};
	JTable tblRx = new JTable(dataRx, colRx);
    String dataTx[][] = {	  
			{"ADV packets",""},
			//{"DAT packets",""},
			{"eACK packets",""},
			{"Retransmissions",""},
			{"Lost races",""}};
    String colTx[] = {"Node Transmission  Statistic",""};
	JTable tblTx = new JTable(dataTx, colTx);
	String dataTime[][] = {
			{"ADV","","","",""},
			{"TIME","","","",""},
			{"eACK","","","",""},
			{"DAT","","","",""}};
    String colTime[] = {"Packet","last TX","last RX","max RX","min RX"};
	JTable tblTime = new JTable(dataTime, colTime);
	String dataTime2[][] = {
			{"Records",""},
			{"Received Bytes",""},
			{"Buffered Bytes",""},
			{"BitsPerSecond (bps)",""},
			{"Loss ratio in %",""}};	  
    String colTime2[] = {"Received by Gateway",""};
	JTable tblGateway = new JTable(dataTime2, colTime2);
	JTextArea txtOut = new JTextArea();
	JScrollPane paneOut;
	/**
	 * @param arg0
	 * @throws HeadlessException
	 */
	public GatewayFrame(String arg0) throws HeadlessException {
		super(arg0);
		
		//GUI construction
		setDefaultLookAndFeelDecorated(true);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		getContentPane().add(contentPane);
		contentPane.setLayout(new BorderLayout(5,5));
		contentPane.setBorder(new EmptyBorder(10,10,10,10));
		
		//com port combo
		Enumeration portList = CommPortIdentifier.getPortIdentifiers();
		CommPortIdentifier portID;
	    while (portList.hasMoreElements()) {
	    	portID = (CommPortIdentifier) portList.nextElement();
	    	if((portID.getPortType() == CommPortIdentifier.PORT_SERIAL))
	    		comboPort.addItem(portID.getName());
	    }
	    
		panNorth.add(comboPort);
		panNorth.setLayout(new FlowLayout(FlowLayout.CENTER));
		panNorth.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), "Gateway"));
		btnOpen.setActionCommand("open");
		btnOpen.addActionListener(this);
		panNorth.add(btnOpen);
		btnConnect.setEnabled(false);
		btnConnect.setActionCommand("connect");
		btnConnect.addActionListener(this);
		panNorth.add(btnConnect);
		txtStatus.setEditable(false);
		txtStatus.setBorder(new BevelBorder(BevelBorder.LOWERED));
		txtStatus.setEnabled(false);
		txtStatus.setPreferredSize(new Dimension(150,20));
		panNorth.add(txtStatus);
		contentPane.add(panNorth, BorderLayout.NORTH);
		
		lstNodes.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		lstNodes.addListSelectionListener(this);
		panLstNodes.setPreferredSize(new Dimension(100,430));
		panWest.add(new JScrollPane(panLstNodes));
		panWest.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), "Nodes"));
		contentPane.add(panWest, BorderLayout.LINE_START);
		
		tblInfo.sizeColumnsToFit(0);
		tblInfo.setPreferredScrollableViewportSize(new Dimension(400,70));
		tblRx.setPreferredScrollableViewportSize(new Dimension(100,50));
		tblRx.sizeColumnsToFit(0);
		tblTx.setPreferredScrollableViewportSize(new Dimension(100,50));
		tblTx.sizeColumnsToFit(0);
		tblGateway.setPreferredScrollableViewportSize(new Dimension(100,70));
		tblGateway.sizeColumnsToFit(0);
		tblTime.setPreferredScrollableViewportSize(new Dimension(100,55));
		contentPane.add(panCenter, BorderLayout.CENTER);
		panCenter.setLayout(new BoxLayout(panCenter ,BoxLayout.Y_AXIS));
		panCenter.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), "Node Details"));
		panCenter.add(new JScrollPane(tblInfo));
		panCenter.add(new JScrollPane(tblRx));
		panCenter.add(new JScrollPane(tblTx));
		panCenter.add(new JScrollPane(tblGateway));
		panCenter.add(new JScrollPane(tblTime));
		
		panSouth.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.LOWERED), "Incomming Messages"));
		txtOut.setEditable(false);
		txtOut.setPreferredSize(new Dimension(500,4000));
		txtOut.setText("<no data available>");
		txtOut.setWrapStyleWord(true);
		paneOut = new JScrollPane(txtOut);
		paneOut.setPreferredSize(new Dimension(550,130));
		panSouth.add(paneOut);
		contentPane.add(panSouth, BorderLayout.SOUTH);
	}

	public void actionPerformed(ActionEvent e) {
		if(e.getActionCommand().equals("open")){

			if(gateway.open((String)comboPort.getSelectedItem())){
				btnConnect.setEnabled(true);
				comboPort.setEnabled(false);
				txtStatus.setEnabled(true);
				btnOpen.setText("Close");
				btnOpen.setActionCommand("close");
			}
			txtOut.setText(gateway.error);
		}
		else if(e.getActionCommand().equals("close")){
			btnOpen.setText("Open");
			if(gateway.close()){
				btnConnect.setEnabled(false);
				comboPort.setEnabled(true);
				txtStatus.setText("not connected");
				txtStatus.setEnabled(false);
				btnConnect.setText("Connect");
				btnOpen.setActionCommand("open");
			}
		}
		else if(e.getActionCommand().equals("connect")){
			btnConnect.setText("Reconnect");
			if(gateway.connect()){
				lstNodes.setListData(new Object[0]);
				nodes.clear();
				node_list.clear();
			}
		}
		//txtOut.setText(gateway.error);
	}
	
	public void valueChanged(ListSelectionEvent e){
		
		Integer intID = (Integer)lstNodes.getSelectedValue();
		if(intID == null)
			return;
		
		NodeRecorder node = gateway.getNode(intID.intValue());
		InfoPacket infPacket;
		if(node != null){
			infPacket = node.infoPacket;
			if(infPacket == null)
				infPacket = new InfoPacket();
			
			if(node.bytes_received>0x100000){
				tblGateway.setValueAt("Received mBytes" , 1, 0);
				tblGateway.setValueAt(String.valueOf(node.bytes_received>>20) , 1, 1);
			}else if(node.bytes_received>0x400){
				tblGateway.setValueAt("Received kBytes" , 1, 0);
				tblGateway.setValueAt(String.valueOf(node.bytes_received>>10) , 1, 1);
			}
			else{
				tblGateway.setValueAt("Received Bytes" , 1, 0);
				tblGateway.setValueAt(String.valueOf(node.bytes_received) , 1, 1);
			}
			
			tblGateway.setValueAt(String.valueOf(node.num_files) , 0, 1);
			tblGateway.setValueAt(String.valueOf(node.bytes_buffered) , 2, 1);
			
			DecimalFormat format = new DecimalFormat("0.00");
			tblGateway.setValueAt(format.format(node.bps) , 3, 1);
			tblGateway.setValueAt(format.format(node.loss_rate) , 4, 1);
		}
		else{
			tblGateway.setValueAt(String.valueOf(0), 0, 1);
			tblGateway.setValueAt("Received Bytes" , 1, 0);
			tblGateway.setValueAt(String.valueOf(0) , 1, 1);
			tblGateway.setValueAt(String.valueOf(0), 2, 1);
			infPacket = new InfoPacket();
		}
		tblInfo.setValueAt(String.valueOf(((Integer)lstNodes.getSelectedValue())) , 0, 1);
		tblInfo.setValueAt(String.valueOf(infPacket.cost) , 1, 1);
		if(infPacket.node_type == InfoPacket.TYPE_GATEWAY)
			tblInfo.setValueAt("gateway" , 2, 1);
		else if(infPacket.node_type == InfoPacket.TYPE_SENSOR)
			tblInfo.setValueAt("sensor" , 2, 1);
		else if(infPacket.node_type == InfoPacket.TYPE_INTERMEDIATE)
			tblInfo.setValueAt("intermediate" , 2, 1);
		else
			tblInfo.setValueAt("awaiting info pkt..." , 2, 1);
		
		tblInfo.setValueAt(String.valueOf(infPacket.neighbours) , 3, 1);
		
		tblInfo.setValueAt(String.valueOf(infPacket.num_errors) , 4, 1);
		tblInfo.setValueAt(String.valueOf(infPacket.last_error) , 5, 1);
		tblInfo.setValueAt(String.valueOf(infPacket.num_warnings) , 6, 1);
		tblInfo.setValueAt(String.valueOf(infPacket.last_warning) , 7, 1);
		tblInfo.setValueAt(String.valueOf(infPacket.child_time_offset_max) , 8, 1);
		tblInfo.setValueAt(String.valueOf(infPacket.time_adv_slot) , 9, 1);
		
		tblRx.setValueAt(String.valueOf(infPacket.num_rx_adv) , 0, 1);
		tblRx.setValueAt(String.valueOf(infPacket.num_rx_time) , 1, 1);
		//tblRx.setValueAt(String.valueOf(infPacket.num_rx_dat) , 2, 1);
		tblRx.setValueAt(String.valueOf(infPacket.num_rx_vAck) , 2, 1);
		tblRx.setValueAt(String.valueOf(infPacket.num_rx_eAck) , 3, 1);
		
		tblTx.setValueAt(String.valueOf(infPacket.num_tx_adv) , 0, 1);
		//tblTx.setValueAt(String.valueOf(infPacket.num_tx_dat) , 1, 1);
		tblTx.setValueAt(String.valueOf(infPacket.num_tx_eAck) , 1, 1);
		tblTx.setValueAt(String.valueOf(infPacket.num_rx_dat - infPacket.num_tx_dat) , 2, 1);
		tblTx.setValueAt(String.valueOf(infPacket.num_lost_races) , 3, 1);
		

		
		tblTime.setValueAt(String.valueOf(infPacket.stime_last_tx_adv), 0, 1);
		tblTime.setValueAt(String.valueOf(infPacket.stime_last_tx_time), 1, 1);
		tblTime.setValueAt(String.valueOf(infPacket.stime_last_tx_eack), 2, 1);
		tblTime.setValueAt(String.valueOf(infPacket.stime_last_tx_dat), 3, 1);
		tblTime.setValueAt(String.valueOf(infPacket.stime_last_rx_adv), 0, 2);
		tblTime.setValueAt(String.valueOf(infPacket.stime_last_rx_time), 1, 2);
		tblTime.setValueAt(String.valueOf(infPacket.stime_last_rx_eack), 2, 2);
		tblTime.setValueAt(String.valueOf(infPacket.stime_last_rx_dat), 3, 2);
		tblTime.setValueAt(String.valueOf(infPacket.stime_max_rx_adv), 0, 3);
		tblTime.setValueAt(String.valueOf(infPacket.stime_max_rx_time), 1, 3);
		tblTime.setValueAt(String.valueOf(infPacket.stime_max_rx_eack), 2, 3);
		tblTime.setValueAt(String.valueOf(infPacket.stime_max_rx_dat), 3, 3);
		tblTime.setValueAt(String.valueOf(infPacket.stime_min_rx_adv), 0, 4);
		tblTime.setValueAt(String.valueOf(infPacket.stime_min_rx_time), 1, 4);
		tblTime.setValueAt(String.valueOf(infPacket.stime_min_rx_eack), 2, 4);
		tblTime.setValueAt(String.valueOf(infPacket.stime_min_rx_dat), 3, 4);
	}
	
	
	/**
	 * is called, if a com port error occured (see InfrasoundGateway.error)
	 */
	public void gatewayErrorEvent(){
		txtOut.setText(gateway.error);
	}
	
	/**
	 * is called, if a node sent a INFO packet
	 * @param nodeID
	 */
	public void newNodeEvent(int id){
		
		if(nodes.contains(String.valueOf(id)))
			return;
	
		nodes.add(String.valueOf(id));
		node_list.add(id);
		lstNodes.setListData(node_list);
		
		if(node_list.size()>1)
			txtStatus.setText("connected");
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		GatewayFrame frame = new GatewayFrame("Infrasound Gateway Client");
		frame.pack();
        frame.setVisible(true);
	}
	
	/**
	 * called, if raw data was received
	 * @param bytes
	 * @param len
	 */
	public void msgEvent(String str){
		// @todo limit str to size of textarea
		
		txtOut.setText(txtOut.getText() + str );
		
		//truncate
		if(txtOut.getText().length()>=4096)
			txtOut.setText(txtOut.getText().substring(2048, txtOut.getText().length() -1));
		
		
		txtOut.moveCaretPosition(txtOut.getText().length());

		if(str.equals("[interest] OK\r\n"))
			txtStatus.setText("waiting for nodes...");
	}
	
	/**
	 * called, if raw data was received
	 * @param bytes
	 * @param len
	 */
	public void dataEvent(int id){
		Integer intID = new Integer(id);
		if(intID.equals((Integer)lstNodes.getSelectedValue())){
			NodeRecorder node = gateway.getNode(id);
			if(node != null){		
				if(node.bytes_received>0x100000){
					tblGateway.setValueAt("Received mBytes" , 1, 0);
					tblGateway.setValueAt(String.valueOf(node.bytes_received>>20) , 1, 1);
				}else if(node.bytes_received>0x400){
					tblGateway.setValueAt("Received kBytes" , 1, 0);
					tblGateway.setValueAt(String.valueOf(node.bytes_received>>10) , 1, 1);
				}
				else{
					tblGateway.setValueAt("Received Bytes" , 1, 0);
					tblGateway.setValueAt(String.valueOf(node.bytes_received) , 1, 1);
				}
				
				tblGateway.setValueAt(String.valueOf(node.num_files) , 0, 1);
				tblGateway.setValueAt(String.valueOf(node.bytes_buffered) , 2, 1);
				
				DecimalFormat format = new DecimalFormat("0.00");
				
				tblGateway.setValueAt(format.format(node.bps) , 3, 1);
				tblGateway.setValueAt(format.format(node.loss_rate) , 4, 1);
			}
		}
	}

}
